// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UI_EVENTS_ANDROID_SCROLLER_H_
#define UI_EVENTS_ANDROID_SCROLLER_H_

#include "base/time/time.h"
#include "ui/events/events_base_export.h"
#include "ui/events/gesture_curve.h"
#include "ui/gfx/geometry/vector2d_f.h"

namespace ui {

// Native port of android.widget.Scroller.
// * Change-Id: I4365946f890a76fcfa78ca9d69f2a8e0848095a9
// * Please update the Change-Id as upstream Android changes are pulled.
class EVENTS_BASE_EXPORT Scroller : public GestureCurve {
public:
    struct Config {
        Config();

        // Controls fling deceleration. Defaults to 0.015f.
        float fling_friction;

        // Controls fling accumulation. Defaults to disabled.
        bool flywheel_enabled;
    };

    explicit Scroller(const Config& config);
    ~Scroller() override;

    // GestureCurve implementation.
    bool ComputeScrollOffset(base::TimeTicks time,
        gfx::Vector2dF* offset,
        gfx::Vector2dF* velocity) override;

    // Start scrolling by providing a starting point and the distance to travel.
    // The default value of 250 milliseconds will be used for the duration.
    void StartScroll(float start_x,
        float start_y,
        float dx,
        float dy,
        base::TimeTicks start_time);

    // Start scrolling by providing a starting point, the distance to travel,
    // and the duration of the scroll.
    void StartScroll(float start_x,
        float start_y,
        float dx,
        float dy,
        base::TimeTicks start_time,
        base::TimeDelta duration);

    // Start scrolling based on a fling gesture. The distance travelled will
    // depend on the initial velocity of the fling.
    void Fling(float start_x,
        float start_y,
        float velocity_x,
        float velocity_y,
        float min_x,
        float max_x,
        float min_y,
        float max_y,
        base::TimeTicks start_time);

    // Extend the scroll animation by |extend|. This allows a running animation
    // to scroll further and longer when used with |SetFinalX()| or |SetFinalY()|.
    void ExtendDuration(base::TimeDelta extend);
    void SetFinalX(float new_x);
    void SetFinalY(float new_y);

    // Stops the animation. Contrary to |ForceFinished()|, aborting the animation
    // causes the scroller to move to the final x and y position.
    void AbortAnimation();

    // Terminate the scroll without affecting the current x and y positions.
    void ForceFinished(bool finished);

    // Returns whether the scroller has finished scrolling.
    bool IsFinished() const;

    // Returns the time elapsed since the beginning of the scrolling.
    base::TimeDelta GetTimePassed() const;

    // Returns how long the scroll event will take.
    base::TimeDelta GetDuration() const;

    float GetStartX() const;
    float GetStartY() const;
    float GetCurrX() const;
    float GetCurrY() const;
    float GetCurrVelocity() const;
    float GetCurrVelocityX() const;
    float GetCurrVelocityY() const;
    float GetFinalX() const;
    float GetFinalY() const;

    bool IsScrollingInDirection(float xvel, float yvel) const;

private:
    enum Mode {
        UNDEFINED,
        SCROLL_MODE,
        FLING_MODE,
    };

    bool ComputeScrollOffsetInternal(base::TimeTicks time);
    void RecomputeDeltas();

    double GetSplineDeceleration(float velocity) const;
    base::TimeDelta GetSplineFlingDuration(float velocity) const;
    double GetSplineFlingDistance(float velocity) const;

    Mode mode_;

    float start_x_;
    float start_y_;
    float final_x_;
    float final_y_;

    float min_x_;
    float max_x_;
    float min_y_;
    float max_y_;

    float curr_x_;
    float curr_y_;
    base::TimeTicks start_time_;
    base::TimeTicks curr_time_;
    base::TimeDelta duration_;
    double duration_seconds_reciprocal_;
    float delta_x_;
    float delta_x_norm_;
    float delta_y_;
    float delta_y_norm_;
    bool finished_;
    bool flywheel_enabled_;

    float velocity_;
    float curr_velocity_;
    float distance_;

    float fling_friction_;
    float deceleration_;
    float tuning_coeff_;
};

} // namespace ui

#endif // UI_EVENTS_ANDROID_SCROLLER_H_
